/** @file    BuildingGeometry.cpp
 *  @author  Alessandro Polo
 *  @version $Id: BuildingGeometry.cpp 2773 2010-07-14 13:26:32Z alex $
 *  @brief
 * File containing methods for the wosh::building::BuildingGeometry class.
 * The header for this class can be found in BuildingGeometry.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <framework/building/BuildingGeometry.h>

 #include <core/ObjectAllocator.h>
 #include <core/Table.h>
 #include <cmath>


using namespace std;
namespace wosh {
 namespace building {

double findMinSample( double value ) {
	double integral, fractional;
	fractional = modf(value, &integral);
	if ( fractional != 0 )
		return fractional;
	return 1.0;
 }
/*
int gcd( int x, int y ) {
	if ( y == 0 ) return x;
	else return gcd(y, x % y);
 }

 public static final int lcm(int[] x) {
      if(x.length<2) {
          throw new Error("Do not use this method if there are less than"+
                          " two numbers.");
      }
      int tmp = lcm(x[x.length-1],x[x.length-2]);
      for(int i=x.length-3; i>=0; i--) {
          if(x[i]<=0) {
              throw new IllegalArgumentException("Cannot compute the least common multiple of several numbers where one, at least, is negative.");
          }
          tmp = lcm(tmp,x[i]);
      }
      return tmp;
  }
*/

 WOSH_REGISTER(wosh::building::BuildingGeometry, "wosh::ISerializable", 1.0, _static_BuildingGeometry )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

BuildingGeometry::BuildingGeometry() {
	this->vortexGrid = NULL;
	clear();
 }

BuildingGeometry::BuildingGeometry( const BuildingGeometry& m ) {
	this->vortexGrid = NULL;
	clear();
	*this = m;
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BuildingGeometry::clear() {
	this->position.x = this->position.y = this->position.z = 0.0;
	this->boundingBox.x = this->boundingBox.y = this->boundingBox.z = 0.0;

	allocVortexGrid(0,0,0);
	this->vortexSize = 0;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool BuildingGeometry::isZero() const {
	if ( this->position.x + this->position.y + this->position.z != 0.0 ) return false;
	if ( this->boundingBox.x + this->boundingBox.y + this->boundingBox.z != 0.0 ) return false;
	return true;
 }

bool BuildingGeometry::is2D() const {
	if ( this->boundingBox.z != 0.0 ) return false;
	if ( this->vortexGridZ <= 1 ) return true;
	return false;
 }

bool BuildingGeometry::is3D() const {
	if ( this->boundingBox.z == 0.0 ) return false;
	if ( this->vortexGridZ > 1 ) return true;
	return false;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BuildingGeometry::setPosition( const Point3D& pos ) {
	this->position = pos;
 }

void BuildingGeometry::setPosition( double x, double y, double z ) {
	this->boundingBox.x = x;
	this->boundingBox.y = y;
	this->boundingBox.z = z;
 }

void BuildingGeometry::setBoundingBox( const Point3D& bb ) {
	this->boundingBox = bb;
 }

void BuildingGeometry::setBoundingBox( double size_x, double size_y, double size_z ) {
	this->boundingBox.x = size_x;
	this->boundingBox.y = size_y;
	this->boundingBox.z = size_z;
 }

WRESULT BuildingGeometry::calculateBoundingBox() {
	if ( this->vortexSize == 0.0 ) return WRET_ERR_ILLEGAL_USE;
	if ( this->vortexGridX + this->vortexGridY + this->vortexGridZ == 0 ) return WRET_ERR_ILLEGAL_USE;
	this->boundingBox.x = this->vortexGridX * vortexSize;
	this->boundingBox.y = this->vortexGridY * vortexSize;
	this->boundingBox.z = this->vortexGridZ * vortexSize;
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BuildingGeometry::autobuildVortexGrid() {
	if ( this->boundingBox.x == 0 || this->boundingBox.y == 0 ) {
		allocVortexGrid(0,0,0);
		return;
	 }
	if ( this->vortexSize == 0 ) {
		double minSample_x = findMinSample( this->boundingBox.x );
		double minSample_y = findMinSample( this->boundingBox.y );
		double minSample_z = findMinSample( this->boundingBox.z );
		double minSample = _GET_Min(minSample_x, minSample_y);
		minSample = _GET_Min(minSample, minSample_z); // buggy, todo calculate gcd! 
		this->vortexSize = minSample;
	 }
	rebuildVortexGrid();
 }


void BuildingGeometry::rebuildVortexGrid() {
	allocVortexGrid(0,0,0);
	if ( this->vortexSize == 0 || this->boundingBox.x == 0 || this->boundingBox.y == 0 )
		return;
	int grid_x = this->boundingBox.x / this->vortexSize;
	int grid_y = this->boundingBox.y / this->vortexSize;
	int grid_z = this->boundingBox.z / this->vortexSize;
	if ( grid_z == 0 ) grid_z = 1;
	allocVortexGrid(grid_x,grid_y,grid_z);
 }

void BuildingGeometry::allocVortexGrid( int X, int Y, int Z ) {
	if ( this->vortexGrid != NULL ) {
		for(int z=0; z<Z; ++z) {
			delete [] this->vortexGrid[z];
		 }
		delete [] this->vortexGrid;
	 }
	this->vortexGrid = NULL;
	this->vortexGridX = this->vortexGridY = this->vortexGridZ = 0;
	if ( X <= 0 || Y <= 0 || Z <= 0 )
		return;

	int count = X*Y;
	this->vortexGrid = new Voxel*[Z];
	for(int z=0; z<Z; ++z) {
		this->vortexGrid[z] = new Voxel[count];
		for(int ind=0; ind<count; ++ind) {
			this->vortexGrid[z][ind].type = 0;
			this->vortexGrid[z][ind].material = 0;
		 }
	 }
	this->vortexGridX = X;
	this->vortexGridY = Y;
	this->vortexGridZ = Z;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

BuildingGeometry& BuildingGeometry::operator=( const BuildingGeometry& m ) {
	if (this == &m) return *this; // same object?
	this->position = m.position;
	this->boundingBox = m.boundingBox;
	this->vortexSize = m.vortexSize;
	allocVortexGrid(m.vortexGridX,m.vortexGridY,m.vortexGridZ);
	int count = this->vortexGridX * this->vortexGridY;
	for(int z=0; z<this->vortexGridZ; ++z) {
		for(int ind=0; ind<count; ++ind) {
			this->vortexGrid[z][ind].type = m.vortexGrid[z][ind].type;
			this->vortexGrid[z][ind].material = m.vortexGrid[z][ind].material;
		 }
	 }
	return *this;
 }

bool BuildingGeometry::operator==( const BuildingGeometry &other ) const {
	if ( this->position.x != other.position.x ) return false;
	if ( this->position.y != other.position.y ) return false;
	if ( this->position.z != other.position.z ) return false;
	if ( this->boundingBox.x != other.boundingBox.x ) return false;
	if ( this->boundingBox.y != other.boundingBox.y ) return false;
	if ( this->boundingBox.z != other.boundingBox.z ) return false;
	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT BuildingGeometry::buildGridFromModel( GEOMETRY_MODEL model, const double* values, int value_count, double rotation ) {
	if ( values == NULL ) return WRET_ERR_PARAM;
	if ( this->vortexSize == 0 )
		this->vortexSize = 0.1; // 10cm
	switch(model) {
		case MODEL_2D_RECT: {
				if ( value_count < 2 ) return WRET_ERR_PARAM;
				setBoundingBox( values[0], values[1], 0 );
				autobuildVortexGrid();
				break;
			 }
		case MODEL_2D_SQUARE: {
				if ( value_count < 1 ) return WRET_ERR_PARAM;
				setBoundingBox( values[0], values[0], 0 );
				autobuildVortexGrid();
				break;
			 }
		case MODEL_2D_CIRCLE: {
				if ( value_count < 1 ) return WRET_ERR_PARAM;
				setBoundingBox( values[0]*2, values[0]*2, 0 );
				autobuildVortexGrid();
				// todo
				break;
			 }
		case MODEL_3D_PRISM_RECT: {
				if ( value_count < 3 ) return WRET_ERR_PARAM;
				setBoundingBox( values[0], values[1], values[2] );
				autobuildVortexGrid();
				break;
			 }
		case MODEL_3D_CUBE: {
				if ( value_count < 1 ) return WRET_ERR_PARAM;
				setBoundingBox( values[0], values[0], values[0] );
				autobuildVortexGrid();
				break;
			 }
		case MODEL_3D_SPHERE: {
				if ( value_count < 1 ) return WRET_ERR_PARAM;
				setBoundingBox( values[0]*2, values[0]*2, values[0]*2 );
				autobuildVortexGrid();
				// todo
				break;
			 }
		case MODEL_UNKNOWN:
		default:
			return WRET_ERR_PARAM;
	 }

	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string BuildingGeometry::toString() const {
	return "BuildingGeometry";
 }

bool BuildingGeometry::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = true;
	ret = hash_context->update( this->position.x ) & ret;
	ret = hash_context->update( this->position.y ) & ret;
	ret = hash_context->update( this->position.z ) & ret;
	ret = hash_context->update( this->boundingBox.x ) & ret;
	ret = hash_context->update( this->boundingBox.y ) & ret;
	ret = hash_context->update( this->boundingBox.z ) & ret;
	ret = hash_context->update( this->vortexSize ) & ret;
	return ret;
 }

const char* BuildingGeometry::getModelAsString( BuildingGeometry::GEOMETRY_MODEL model ) {
	switch(model) {
		case MODEL_2D_RECT:			return "2D_RECT";
		case MODEL_2D_SQUARE:		return "2D_SQUARE";
		case MODEL_2D_POLYGON:		return "2D_POLYGON";
		case MODEL_2D_CIRCLE:		return "2D_CIRCLE";
		case MODEL_3D_PRISM_RECT:	return "3D_PRISM_RECT";
		case MODEL_3D_CUBE:			return "3D_CUBE";
		case MODEL_3D_SPHERE:		return "3D_SPHERE";
		case MODEL_UNKNOWN:
		default:					return "UNKNOWN";
	 }
 }

BuildingGeometry::GEOMETRY_MODEL BuildingGeometry::getModelFromString( const char* model ) {
	if ( model == NULL ) return MODEL_UNKNOWN;
	if ( strcmp(model,"2D_RECT") == 0 ) return MODEL_2D_RECT;
	if ( strcmp(model,"2D_SQUARE") == 0 ) return MODEL_2D_SQUARE;
	if ( strcmp(model,"2D_POLYGON") == 0 ) return MODEL_2D_POLYGON;
	if ( strcmp(model,"2D_CIRCLE") == 0 ) return MODEL_2D_CIRCLE;
	if ( strcmp(model,"3D_PRISM_RECT") == 0 ) return MODEL_3D_PRISM_RECT;
	if ( strcmp(model,"3D_CUBE") == 0 ) return MODEL_3D_CUBE;
	if ( strcmp(model,"3D_SPHERE") == 0 ) return MODEL_3D_SPHERE;
	return MODEL_UNKNOWN;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace building
}; // namespace wosh
